package main

import (
	"fmt"
	"github.com/ikaiguang/go-sqlparser"
	"log"
	"strings"
)

type AnalysisMysql struct {
	Sql      string
	SplitSql []string
	Error    []string
	KeyWords  []string
	ddl *sqlparser.DDL
	primaryKeyName string
}

func NewAnalysisMysql(sql string) *AnalysisMysql {
	return &AnalysisMysql{Sql: sql}
}

func (a *AnalysisMysql) Parse() *AnalysisMysql {
	stat, err := sqlparser.ParseStrictDDL(a.Sql)
	fmt.Println(stat)
	if err != nil {
		a.AddError(err.Error())
		log.Println("ParseErr:", err)
		return a
	}
	ddl, ok := stat.(*sqlparser.DDL)
	if !ok {
		a.AddError("sql 不是ddl语句，请检查后重新校验！")
		log.Println("stat.(*sqlparser.DDL) convert fail")
		return a
	}
	if ddl.NewName.Name.String() == ""{
		a.AddError("sql解析失败")
	}
	fmt.Printf("ddl:   %+v \r\n ", ddl)
	a.ddl = ddl
	return a
}

func (a *AnalysisMysql) AnalysisField() *AnalysisMysql {
	if a.ddl == nil {
		return a
	}
	if a.ddl.TableSpec == nil {
		return a
	}
	var autoStatus = false
	if a.ddl.TableSpec.Columns != nil {
		for _,v := range a.ddl.TableSpec.Columns {
			if a.InArray(v.Name.String(), a.KeyWords) {
				a.AddError("字段:"+v.Name.String()+"为mysql关键词")
			}
			if v.Type.Default != nil  && string(v.Type.Default.Val) == "null"{
				a.AddError("字段:"+v.Name.String()+"默认值为null")
			}else if v.Type.Default == nil && !v.Type.Autoincrement{
				a.AddError("字段:"+v.Name.String()+"默认值没有设置")
			}

			if v.Type.Comment != nil && (string(v.Type.Comment.Val) == "null" || string(v.Type.Comment.Val) == "") {
				a.AddError("字段:"+v.Name.String()+"备注信息不能为空")
			}else if v.Type.Comment == nil{
				a.AddError("字段:"+v.Name.String()+"备注信息没有设置")
			}

			if v.Type.Autoincrement {
				autoStatus = true
			}
			var charsetList = []string{"utf8", "UTF8", "UTF8MB4", "utf8mb4"}
			if !a.InArray(v.Type.Charset, charsetList) && v.Type.Charset != ""{
				a.AddError("字段:"+v.Name.String()+"字符集不是utf8")
			}
		}
		if !autoStatus {
			a.AddError("主键没有设置自增")
		}
	}
	return a
}

func (a *AnalysisMysql) AnalysisIndex() *AnalysisMysql  {
	if a.ddl == nil {
		return a
	}
	if a.ddl.TableSpec == nil {
		return a
	}
	var primaryKeyStatus = 0
	if len(a.ddl.TableSpec.Indexes) == 0 {
		primaryKeyStatus = 0
	}else if len(a.ddl.TableSpec.Indexes) > 5{
		a.AddError("索引个数超过5个，请知晓")
	}
	for _,v := range a.ddl.TableSpec.Indexes {
		if v.Info.Primary {
			primaryKeyStatus = 1
		}
		for _,v := range v.Columns {
			a.primaryKeyName = v.Column.String()
		}
	}
	if primaryKeyStatus == 0 {
		a.AddError("没有设置主键，请检查！")
	}
	return a
}

func (a *AnalysisMysql) AnalysisTable() *AnalysisMysql {
	tableOptions := strings.Split(a.ddl.TableSpec.Options, " ")
	var isEngine = false
	var isAutoIncrement = false
	var isCharset= false
	var isComment = false
	for _,v := range tableOptions {
		vTmp := strings.Trim(v, " ")
		if strings.ToLower(vTmp) == "default"{
			continue
		}
		vTmpS := strings.Split(vTmp, "=")
		if len(vTmpS) > 1 {
			if strings.ToLower(strings.Trim(vTmpS[0], " "))  == "engine"{
				isEngine = true
				if strings.ToLower(strings.Trim(vTmpS[1], " ")) != "innodb"{
					a.AddError("数据库存储引擎选择的不是innodb")
				}
			}else if strings.ToLower(strings.Trim(vTmpS[0], " "))  == "auto_increment"{
				isAutoIncrement = true
				if strings.ToLower(strings.Trim(vTmpS[1], " ")) != "1"{
					a.AddError("auto_increment的值不是1")
				}
			}else if strings.ToLower(strings.Trim(vTmpS[0], " "))  == "charset"{
				isCharset = true
				if !a.InArray(strings.ToLower(strings.Trim(vTmpS[1], " ")), []string{"utf8", "utf8mb4"}) {
					a.AddError("字符集不是utf8")
				}
			}else if strings.ToLower(strings.Trim(vTmpS[0], " "))  == "comment"{
				isComment = true
				if  strings.Trim(vTmpS[1], " ") == "" {
					a.AddError("表备注信息不能为空")
				}
			}
		}
	}
	if !isEngine {
		a.AddError("数据库存储引擎没有设置")
	}else if !isCharset {
		a.AddError("字符集没有设置")
	}else if !isComment {
		a.AddError("表备注信息没有设置")
	}else if !isAutoIncrement {
		a.AddError("auto_increment没有设置")
	}
	return a
}

func (a *AnalysisMysql) Analysis () []string {
	a.addKeyWord()
	a.Parse().AnalysisField().AnalysisIndex().AnalysisTable()

	return a.Error
}

func (a *AnalysisMysql) AddError (s string) {
	a.Error = append(a.Error, s)
}

func (this *AnalysisMysql) addKeyWord() {
	this.KeyWords = []string{"name", "code", "status", "add", "all", "alter", "analyze", "and", "as", "asc", "asensitive", "before", "between", "bigint", "binary", "blob", "both", "by", "call", "cascade", "case", "change", "char", "character", "check", "collate", "column", "condition", "connection", "constraint", "continue", "convert", "create", "cross", "current_date", "current_time", "current_timestamp", "current_user", "cursor", "database", "databases", "day_hour", "day_microsecond", "day_minute", "day_second", "dec", "decimal", "declare", "default", "delayed", "delete", "desc", "describe", "deterministic", "distinct", "distinctrow", "div", "double", "drop", "dual", "each", "else", "elseif", "enclosed", "escaped", "exists", "exit", "explain", "false", "fetch", "float", "float4", "float8", "for", "force", "foreign", "from", "fulltext", "goto", "grant", "group", "having", "high_priority", "hour_microsecond", "hour_minute", "hour_second", "if", "ignore", "in", "index", "infile", "inner", "inout", "insensitive", "insert", "int", "int1", "int2", "int3", "int4", "int8", "integer", "interval", "into", "is", "iterate", "join", "key", "keys", "kill", "label", "leading", "leave", "left", "like", "limit", "linear", "lines", "load", "localtime", "localtimestamp", "lock", "long", "longblob", "longtext", "loop", "low_priority", "match", "mediumblob", "mediumint", "mediumtext", "middleint", "minute_microsecond", "minute_second", "mod", "modifies", "natural", "not", "no_write_to_binlog", "null", "numeric", "on", "optimize", "option", "optionally", "or", "order", "out", "outer", "outfile", "precision", "primary", "procedure", "purge", "raid0", "range", "read", "reads", "real", "references", "regexp", "release", "rename", "repeat", "replace", "require", "restrict", "return", "revoke", "right", "rlike", "schema", "schemas", "second_microsecond", "select", "sensitive", "separator", "set", "show", "smallint", "spatial", "specific", "sql", "sqlexception", "sqlstate", "sqlwarning", "sql_big_result", "sql_calc_found_rows", "sql_small_result", "ssl", "starting", "straight_join", "table", "terminated", "then", "tinyblob", "tinyint", "tinytext", "to", "trailing", "trigger", "true", "undo", "union", "unique", "unlock", "unsigned", "update", "usage", "use", "using", "utc_date", "utc_time", "utc_timestamp", "values", "varbinary", "varchar", "varcharacter", "varying", "when", "where", "while", "with", "write", "x509", "xor", "year_month", "zerofill"}
}









func (a *AnalysisMysql) test(){
	ddl := a.ddl
	//fmt.Println(ddl.Action)  //create
	//fmt.Println(ddl.Table)  //
	//fmt.Println(ddl.IfExists)  //false
	//fmt.Println(ddl.NewName)  // {vd_account_users_relation }
	//fmt.Println(ddl.PartitionSpec) // {Columns:[0xc0000dc160 0xc0000dc210 0xc0000dc2c0 0xc0000dc370
	fmt.Printf("%+v \r\n ", ddl.TableSpec)
	fmt.Println(ddl.TableSpec.Columns)
	for k, v := range ddl.TableSpec.Columns {
		fmt.Println(v.Name)
		fmt.Println("column:", k, v)
	}
	fmt.Println(ddl.TableSpec.Options)
	fmt.Println(ddl.TableSpec.Indexes)
	fmt.Println(ddl.VindexCols)
	fmt.Println(ddl.VindexSpec)
}

func (this *AnalysisMysql) InArray(key string, arr []string) bool {
	for _, v := range arr {
		if key == v {
			return true
		}
	}
	return false
}
